package znet

import (
	"crypto/md5"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"sync"
)

// FileMetadata 文件元数据
type FileMetadata struct {
	FileName   string `json:"fileName"`   // 文件名
	FileSize   int64  `json:"fileSize"`   // 文件大小
	FileMD5    string `json:"fileMD5"`    // 文件MD5
	ChunkCount int    `json:"chunkCount"` // 分块数量
	UploadPath string `json:"uploadPath"` // 上传路径
}

// FileChunk 文件块数据
type FileChunk struct {
	FileID     string `json:"fileId"`     // 文件唯一标识
	ChunkIndex int    `json:"chunkIndex"` // 块索引
	ChunkData  []byte `json:"chunkData"`  // 块数据
}

// FileTransferManager 文件传输管理器
type FileTransferManager struct {
	sync.RWMutex
	uploadPath    string                   // 上传文件保存路径
	activeUploads map[string]*FileMetadata // 活动上传任务
	chunkBuffers  map[string][][]byte      // 文件块缓存
}

var (
	fileManager *FileTransferManager
	once        sync.Once
)

// GetFileManager 获取文件管理器单例
func GetFileManager() *FileTransferManager {
	once.Do(func() {
		fileManager = &FileTransferManager{
			uploadPath:    "uploads",
			activeUploads: make(map[string]*FileMetadata),
			chunkBuffers:  make(map[string][][]byte),
		}
		// 确保上传目录存在
		os.MkdirAll(fileManager.uploadPath, 0755)
	})
	return fileManager
}

// StartUpload 开始文件上传
func (fm *FileTransferManager) StartUpload(metadata *FileMetadata) error {
	fm.Lock()
	defer fm.Unlock()

	// 生成文件ID
	fileID := fmt.Sprintf("%x", md5.Sum([]byte(metadata.FileName+metadata.FileMD5)))

	// 初始化文件块缓存
	fm.chunkBuffers[fileID] = make([][]byte, metadata.ChunkCount)

	// 保存元数据
	metadata.UploadPath = filepath.Join(fm.uploadPath, fileID)
	fm.activeUploads[fileID] = metadata

	return nil
}

// UploadChunk 上传文件块
func (fm *FileTransferManager) UploadChunk(chunk *FileChunk) error {
	fm.Lock()
	defer fm.Unlock()

	// 检查是否存在上传任务
	metadata, exists := fm.activeUploads[chunk.FileID]
	if !exists {
		return fmt.Errorf("UploadChunk:upload task not found: %s", chunk.FileID)
	}

	// 检查块索引是否有效
	if chunk.ChunkIndex < 0 || chunk.ChunkIndex >= metadata.ChunkCount {
		return fmt.Errorf("UploadChunk:invalid chunk index: %d", chunk.ChunkIndex)
	}

	// 保存块数据
	fm.chunkBuffers[chunk.FileID][chunk.ChunkIndex] = chunk.ChunkData

	return nil
}

// FinishUpload 完成文件上传
func (fm *FileTransferManager) FinishUpload(fileID string) error {
	fm.Lock()
	defer fm.Unlock()

	// 获取元数据
	metadata, exists := fm.activeUploads[fileID]
	if !exists {
		return fmt.Errorf("FinishUpload:upload task not found: %s", fileID)
	}

	// 创建文件
	file, err := os.Create(metadata.UploadPath)
	if err != nil {
		return fmt.Errorf("FinishUpload:failed to create file: %v", err)
	}
	defer file.Close()

	// 写入所有块数据
	chunks := fm.chunkBuffers[fileID]
	for _, chunk := range chunks {
		if _, err := file.Write(chunk); err != nil {
			return fmt.Errorf("FinishUpload:failed to write chunk: %v", err)
		}
	}

	// 验证文件MD5
	if err := fm.verifyFileMD5(metadata); err != nil {
		os.Remove(metadata.UploadPath)
		return err
	}

	// 清理资源
	delete(fm.activeUploads, fileID)
	delete(fm.chunkBuffers, fileID)

	return nil
}

// verifyFileMD5 验证文件MD5
func (fm *FileTransferManager) verifyFileMD5(metadata *FileMetadata) error {
	file, err := os.Open(metadata.UploadPath)
	if err != nil {
		return err
	}
	defer file.Close()

	hash := md5.New()
	if _, err := io.Copy(hash, file); err != nil {
		return err
	}

	actualMD5 := fmt.Sprintf("%x", hash.Sum(nil))
	if actualMD5 != metadata.FileMD5 {
		return fmt.Errorf("MD5 mismatch: expected %s, got %s", metadata.FileMD5, actualMD5)
	}

	return nil
}
